package monitor

import (
	"fmt"
	"log"

	"hxy/repo/database"
	. "hxy/repo/model"

	"github.com/go-git/go-git/v5"
	"github.com/go-git/go-git/v5/plumbing/transport/http"
	"github.com/google/uuid"
	"github.com/jakecoffman/cron"
	"gorm.io/gorm"
)

// List all git manager in running
func GetAllGitMonitor() ([]GitMonitorInter, *gorm.DB) {
	return database.GetAllGitMonitor()
}
func GetMonotirInMap() []GitMonitorInter {
	var list = make([]GitMonitorInter, len(managerPool))
	var i = 0
	for _, v := range managerPool {
		list[i] = v.Conter2Interface()
		i++
	}
	return list
}

func (gm *GitManager) UpdateMonitorState(g GitStatus) error {
	gm.Status = g
	gm.Repo.Status = gm.Status.Tostring()
	return database.ChangeGitRepoState(gm.Repo).Error
}

// Create a git monitor for a git manager
// TODO: set up the time gap of the git repo monitor
func (gm *GitManager) CreateCornMonitor() error {
	uid := uuid.New()
	gm.uid = uid
	log.Printf("gm id %s", gm.uid.String())
	remote, err := gm.Repo.GetRemote()
	if err != nil {
		return err
	}
	rp, err := gm.Repo.OpenRepo()
	if err != nil {
		return err
	}

	gm.UpdateMonitorState(Moniting)

	gm.Job = *cron.New()
	gm.Job.AddFunc("@every 10s", func() {
		gm.UpdateMonitorState(Moniting)
		refs, err := remote.List(&git.ListOptions{
			Auth: &http.BasicAuth{
				Username: gm.Auth.Username,
				Password: gm.Auth.Password,
			},
		})
		if err != nil {
			log.Printf("Git Monitor Error:%s", err)
			gm.UpdateMonitorState(GITERROR)
			return
		}
		if len(refs) <= 0 {
			log.Printf("Git Monitor Error:%s", "no refs found..")
			gm.UpdateMonitorState(GITERROR)
			return
		}
		remote_head := refs[0]
		head, err := rp.Head()
		if err != nil {
			log.Printf("Git Monitor Error:%s", err)
			gm.UpdateMonitorState(GITERROR)
			return
		}
		if remote_head.Hash().String() != "0000000000000000000000000000000000000000" &&
			remote_head.Hash().String() != head.Hash().String() {
			gm.UpdateMonitorState(FoundNew)
			log.Printf("detect change, remote:%s ,now head :%s", remote_head.Hash().String(), head.Hash().String())

			worktree, _ := rp.Worktree()
			gm.UpdateMonitorState(PULLING)
			worktree.Pull(&git.PullOptions{
				Auth: &http.BasicAuth{
					Username: gm.Auth.Username,
					Password: gm.Auth.Password,
				},
			})

			gm.UpdateMonitorState(Moniting)

			log.Printf("Trigger new Ci for git id : %d", gm.Repo.ID)
			triggerByManager(gm)
		}

	}, uid.String())
	gm.Job.Start()

	return nil

}

type NotFoundGitManager struct {
	uid uuid.UUID
}

func (e NotFoundGitManager) Error() string {
	return fmt.Sprintf("Can't found the GitManager by Given UUID : %s", e.uid.String())
}

func getGitManager(uid uuid.UUID) (*GitManager, error) {
	gm, ok := managerPool[uid]

	//assert.Equal(uid,gm.uid)
	if !ok {
		return &GitManager{}, &NotFoundGitManager{uid: uid}
	}
	return gm, nil

}

// Stop the Git Corn job by given uuid
func StopCornMonitor(uid uuid.UUID) error {
	gm, err := getGitManager(uid)
	if err != nil {
		return err
	} else {
		gm.Job.Stop()

		return nil
	}

}

// After fount a new commit in the remote ,
// just call the `git pull` command to update the code files
func PullRepoById(uid uuid.UUID) error {
	gm, err := getGitManager(uid)
	//GitManager
	if err != nil {
		return err
	}
	r, err := gm.Repo.OpenRepo()
	//Repostority
	if err != nil {
		return err
	}
	worktree, err := r.Worktree()
	// WorkTree for Git
	if err != nil {
		return err
	}
	gm.Status = PULLING
	ferr := worktree.Pull(&git.PullOptions{
		Auth: &http.BasicAuth{
			Username: gm.Auth.Username,
			Password: gm.Auth.Password,
		},
	})
	if ferr == nil {
		gm.Status = Moniting
	}
	return ferr
}

func (gm GitManager) CloneRepos() (*git.Repository, error) {

	var err error
	var r *git.Repository
	var errcount int = 0
	for {

		errcount++
		r, err = git.PlainClone(gm.Repo.Path, false, &git.CloneOptions{
			URL:               gm.Repo.Url,
			RecurseSubmodules: git.DefaultSubmoduleRecursionDepth,
			Auth: &http.BasicAuth{
				Username: gm.Auth.Username,
				Password: gm.Auth.Password,
			},
		})

		if err == nil {
			break
		}
		if errcount > 10 {
			log.Printf("Git CLONE ERR in 10 try,git id :%d,Project name :%s", gm.Repo.ID, gm.Repo.Name)
			break
		}
	}
	//TODO: add monitor
	go add_git_monitor(gm.Repo)
	return r, err
}
